package com.hp.bon.sgw.wh.processor;

import static com.hp.bon.sgw.Constants.LOG;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.bon.security.Constants.DiameterAVP;
import com.hp.bon.sgw.Constants;
import com.hp.bon.sgw.Constants.ResultCode;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.convert.Xml2AVPConvetor;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.core.ChannelServer;
import com.hp.bon.sgw.core.InprogressMessageQueue;
import com.hp.bon.sgw.core.processor.Processor;
import com.hp.bon.sgw.core.route.RouteInfo;
import com.hp.bon.sgw.domain.ConvMapInfo;
import com.hp.bon.sgw.service.ConfigService;
import com.hp.bon.sgw.util.Comutils;
import com.hp.bon.sgw.util.dcc.DccAvpHelper;
import com.hp.bon.sgw.wh.service.LimitConfigService;
import com.hp.bon.sgw.wh.service.UsageLimitConfigService;
import com.hp.ocs.diameter.message.AVP;
import com.hp.ocs.diameter.message.AVP_Grouped;
import com.hp.ocs.diameter.message.AVP_UTF8String;
import com.hp.ocs.diameter.message.AVP_Unsigned32;
import com.hp.ocs.diameter.message.InvalidAVPLengthException;
import com.hp.ocs.diameter.message.Message;
import com.hp.ocs.diameter.message.MessageDictionary;

public class UsageDccProcessor extends Processor{
	
	private static ConcurrentHashMap<String, String> productOfferIdMap = new ConcurrentHashMap<String, String>();
	
	/**
	 * Logger for this class
	 */
	private static final Logger logger = LoggerFactory.getLogger(FlowDccProcessor.class);
	
	private long time=1000L;
	
	private static ConcurrentHashMap<String, Integer> countMap = new ConcurrentHashMap<String, Integer>();
	
	private UsageLimitConfigService usageLimitConfigService ;
	

	public UsageLimitConfigService getUsageLimitConfigService() {
		return usageLimitConfigService;
	}

	public void setUsageLimitConfigService(
			UsageLimitConfigService usageLimitConfigService) {
		this.usageLimitConfigService = usageLimitConfigService;
	}

	public void shutdown() {
		dccProcessor.interrupt();
	}
	DccProcessor dccProcessor;
	public void init(){
		dccProcessor=new DccProcessor();
		dccProcessor.start();
	}
	
	class DccProcessor extends Thread{

		@Override
		public void run() {
			while (true) {
				try {
					time=usageLimitConfigService.getLongValue("limit_config_time", 1000L);
					countMap.clear();
					logger.info("clear UsageMap time:"+time);
					sleep(time);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
			}
		}
		
	}
	
	public  synchronized Integer putValue(String key) {
		Integer i =countMap.get(key);
		logger.info("asd:"+i);
		if (i==null) {
			countMap.put(key, 1);
			i = 1;
		}else{
			i++;
			countMap.put(key,i);
		}
		return i;
	}
	
	@Override
	public int process(CallParam callParam) {
		
		Message request = (Message) callParam.getRequest();
		AVP avp = null;
		String accNbr="" ,billingCycle="";
		long destinationAttr = -1;
		
		AVP sessionIDAVP = request.find(Constants.DIAMETER_AVP_SESSION_ID);
		String sessionID = AVP_UTF8String.queryValue(sessionIDAVP);
		
		AVP orighHostAVP = request.find(Constants.DIAMETER_AVP_ORIGIN_HOST);
		String orinHost = AVP_UTF8String.queryValue(orighHostAVP);
		
		Integer persent =putValue(orinHost);
		int MIN = usageLimitConfigService.getIntValue(orinHost, 0);
		
		logger.info("sessionID:"+sessionID+"  host:"+orinHost+" persent:"+persent+" MIN："+MIN);
		
		if (MIN > 0) {
			if (persent > MIN) {
				LOG.warn("DCC channel too busy,RecvQueuePercent=" + persent
						+ ",warnRatio=" + MIN + ",sessionId=" + sessionID);
				callParam.setResultMsg("DCC channel too busy,RecvQueuePercent="
						+ persent + ",warnRatio=" + MIN);
				callParam.setRetCode(usageLimitConfigService.getIntValue(
						"DIAMETER_RESULT_TOO_BUSY", 2005));
				
				this.proccessResponse(callParam);
				return 0;
			}
		}
		
		avp = request.find(Constants.DIAMETER_AVP_SERVICE_INFOMATION, Constants.DIAMETER_VENDOR_3GPP);
		if (avp != null) {
			try {
				AVP_Grouped serviceInfsAVP = new AVP_Grouped(avp);

				AVP avp_billInf = serviceInfsAVP.find(80700, Constants.DIAMETER_VENDOR_CT);
				if (avp_billInf != null) {
					AVP_Grouped billInfsAVP = new AVP_Grouped(avp_billInf);
					AVP avp_accNbr = billInfsAVP.find(80125, Constants.DIAMETER_VENDOR_CT);
					if(avp_accNbr != null){
						accNbr = AVP_UTF8String.queryValue(avp_accNbr);
					}
					
					AVP avp_billingCycle = billInfsAVP.find(5043, Constants.DIAMETER_VENDOR_CT);
					if(avp_billingCycle != null){
						billingCycle = AVP_UTF8String.queryValue(avp_billingCycle);
					}
					
					AVP avp_destinationAttr = billInfsAVP.find(5055, Constants.DIAMETER_VENDOR_CT);
					if(avp_destinationAttr != null){
						destinationAttr = AVP_Unsigned32.queryValue(avp_destinationAttr);
					}
					
					AVP avp_productOfferId = billInfsAVP.find(82011, Constants.DIAMETER_VENDOR_CT);
					if(avp_productOfferId != null){
						productOfferIdMap.put(callParam.getSessionId()+"A", AVP_UTF8String.queryValue(avp_productOfferId));
					}
					
				} else {
					LOG.warn(callParam.getSessionId() + " Billing-Information is null.");
					callParam.setRetCode(Constants.ResultCode.MISSING_TAG);
					callParam.setResultMsg("Billing-Information is null");
				}
			} catch (InvalidAVPLengthException e) {
				LOG.warn("AVP Undefined or not found.", e);
				callParam.setRetCode(ResultCode.VALUE_ERROR);
				callParam.setResultMsg("UsageDccProcessor InvalidAVPLengthException:" + e.getLocalizedMessage());
			}
		}
		
		StringBuilder sb = new StringBuilder();
		sb.append("<Service_Information>")
			.append("<Bill_Information>") ;

		if(accNbr != null && !"".equals(accNbr)){
			sb.append("<Acc_Nbr>").append(accNbr).append("</Acc_Nbr>");
		}
		if(billingCycle != null && !"".equals(billingCycle)){
			sb.append("<Billing_Cycle>").append(billingCycle).append("</Billing_Cycle>");
		}
		if(destinationAttr != -1 && !"".equals(destinationAttr)){
			sb.append("<DestinationAttr>").append(destinationAttr).append("</DestinationAttr>");
		}
		
			
		sb.append("</Bill_Information>")
			.append("</Service_Information>");
		
		this.sendReqMsg(callParam, sb.toString() ,"1" ,"RatableAuth.HSS@027.ctnbc-bon.net");
//		BusinessRecordCacheManager.getInstance().add(callParam1);
//		SpringContext.getInstance().getOuterDBDataService().saveToDB(SGWDataStoreService.convertCallParamToBusinessRecord((CallParam) oneCallParam));
		
		return 0 ;
		
	}
	
	@Override
	public int endProcess(CallParam callParam) {
		System.out.println("callParam.getRetCode():"+callParam.getRetCode());
		if(callParam.getRetCode()!=Constants.DIAMETER_SERVICE_RESULT_SUCCESS){
			callParam.getAsynHandler().onCallResult(parseResponse1(callParam));
			return 0 ;
		}
		XmlMessage request = (XmlMessage) callParam.getRequest();
		String data = request.getDATA();
		String accNbr="" ,billingCycle="" ,destinationAttr="";

		Document doc = null;
		try {
			doc = DocumentHelper.parseText(data);
			Element root = doc.getRootElement();
			
			org.dom4j.Node node = root.selectSingleNode("//Service_Information/Bill_Information/Acc_Nbr");
			if (node != null) {
				accNbr = node.getStringValue();
			}
			
			node = root.selectSingleNode("//Service_Information/Bill_Information/Billing_Cycle");
			if (node != null) {
				billingCycle = node.getStringValue();
			}
			
			node = root.selectSingleNode("//Service_Information/Bill_Information/DestinationAttr");
			if (node != null) {
				destinationAttr = node.getStringValue();
			}
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		
		StringBuilder sb = new StringBuilder();
		sb.append("<Service_Information>")
			.append("<Bill_Information>") ;

		if(accNbr != null && !"".equals(accNbr)){
			sb.append("<Acc_Nbr>").append(accNbr).append("</Acc_Nbr>");
		}
		if(billingCycle != null && !"".equals(billingCycle)){
			sb.append("<Billing_Cycle>").append(billingCycle).append("</Billing_Cycle>");
		}
		if(destinationAttr != null && !"".equals(destinationAttr)){
			sb.append("<DestinationAttr>").append(destinationAttr).append("</DestinationAttr>");
		}
		LOG.debug("2"+callParam.getSessionId());
		if (productOfferIdMap.get(callParam.getSessionId()) != null && !"".equals(productOfferIdMap.get(callParam.getSessionId()))) {
			sb.append("<Product_Offer_Id>").append(productOfferIdMap.get(callParam.getSessionId())).append("</Product_Offer_Id>");
			productOfferIdMap.remove(callParam.getSessionId());
		}
			
		sb.append("</Bill_Information>")
			.append("</Service_Information>");
		
		String sci = callParam.getServiceContextId();

		Map<String, ConvMapInfo> allAVPMap = Xml2AVPConvetor.getInstance().getALLNamesMap();
		Message RatableAuthResponse = (Message) callParam.getRecvResponse();
		String isHbFlag = DccAvpHelper.getAVPValue(RatableAuthResponse, "Service_Information/Auth_Group/is_hb_flag", allAVPMap);
		String homeAreaCode = DccAvpHelper.getAVPValue(RatableAuthResponse, "Service_Information/Auth_Group/HomeAreaCode", allAVPMap);
		
		
		if("".equals(isHbFlag) || "".equals(homeAreaCode)){
			callParam.getAsynHandler().onCallResult(parseResponse(callParam));
//			BusinessRecordCacheManager.getInstance().add(parseResponse(callParam));
//			SpringContext.getInstance().getOuterDBDataService().saveToDB(SGWDataStoreService.convertCallParamToBusinessRecord((CallParam) twoCallParam));
			return 0 ;
		}
		
		if (sci.equals("RatableResourceQuery.ABMWH@027.ctnbc-bon.net")
				|| sci.equals("RatableResourceQuery.ABMHB@027.ctnbc-bon.net")
				|| sci.equals("RatableResourceQuery.ABM@027.ctnbc-bon.net")) {
			callParam.getAsynHandler().onCallResult(parseResponse(callParam));
//			BusinessRecordCacheManager.getInstance().add(cp);
		}
		if(sci.equals("RatableAuth.HSS@027.ctnbc-bon.net")){//预后判断
//			BusinessRecordCacheManager.getInstance().add(callParam);
			if(isHbFlag.equals("1") && homeAreaCode.equals("1001")){
				this.sendReqMsg(callParam, sb.toString() ,"2" ,"RatableResourceQuery.ABMWH@027.ctnbc-bon.net");
			} else if(isHbFlag.equals("1") && !homeAreaCode.equals("1001")){
				this.sendReqMsg(callParam, sb.toString() ,"2" ,"RatableResourceQuery.ABMHB@027.ctnbc-bon.net");
			}else{
				this.sendReqMsg(callParam, sb.toString() ,"2" ,"RatableResourceQuery.ABM@027.ctnbc-bon.net");
			}
		}
		return 0;
	}
	
	private CallParam sendReqMsg(CallParam callParam ,String data ,String sendCount ,String sci)	{
		
		
		String sessionId2 = callParam.getSessionId()+"A";

		XmlMessage req = new XmlMessage(true);
		req.setDATA(data);
		try {
			req.setDataElement(Comutils.parseText(data).getRootElement());
		} catch (Exception e) {
			e.printStackTrace();
		}
		req.setMID(sessionId2);
		req.setUID(callParam.getUserName());
		req.setSCID(sci);
		CallParam newCallParam = new CallParam(sessionId2, sci, req, callParam.getUserName(), callParam.getAsynHandler());
		newCallParam.setBussinessCallId(callParam.getBussinessCallId());
		newCallParam.setProcessor(this);
		RouteInfo r = ChannelServer.findRoute(newCallParam.getServiceContextId());
		int ret = 0;
		String errMsg = null;
		if (r == null) {
			errMsg = "No Route for " + newCallParam.getServiceContextId();
			ret = 3002;// 没有查到路由
		} else if (r.result != 0) {
			errMsg = "Route unavailable for " + newCallParam.getServiceContextId();
			ret = r.result;
		}
		if (ret != 0) {
			newCallParam.setRetCode(3002);
			newCallParam.setResultMsg(errMsg);
			return newCallParam;
		}
		newCallParam.setToNode(r.curNode);
		InprogressMessageQueue.getInstance().addRequest(newCallParam);
		r.curNode.getChannel().sendRequest(newCallParam);
		return newCallParam;
	}
	
	public CallParam parseResponse(CallParam callParam) {

		Message request = (Message) callParam.getRecvResponse();
		Message answer = new Message();
		answer.prepareResponse(request);

		String sessionId = callParam.getSessionId();
		sessionId = sessionId.substring(0,sessionId.length()-2);
		
		request.updateAVP_UTF8String("263", sessionId);
		
		CallParam newcallParam = new CallParam(sessionId, callParam.getServiceContextId(), request, callParam.getUserName(), callParam.getAsynHandler());
		newcallParam.setResponse(request);
		
		return newcallParam;
	}
	
	public CallParam parseResponse1(CallParam callParam) {
		
//		((Message)callParam.getSendRequest()).getDictionary().parseToString()
		
		if(callParam.getRecvResponse()==null){
			Message request =(Message)callParam.getSendRequest();
			callParam.setRequest(request);
			Message answer = new Message();
			answer.prepareResponse(request);
			MessageDictionary md = request.getDictionary();
			md.prepareResponseAVP(answer);
			
			
			Iterator<AVP> it = answer.iterator(Constants.DIAMETER_AVP_ORIGIN_HOST);
			while (it.hasNext()) it.remove();
			it = answer.iterator(Constants.DIAMETER_AVP_ORIGIN_REALM);
			while (it.hasNext()) it.remove();
			it = answer.iterator(263);
			while (it.hasNext()) it.remove();
			
			answer.add(new AVP_UTF8String(Constants.DIAMETER_AVP_ORIGIN_HOST,ConfigService.sgwHostId));
			answer.add(new AVP_UTF8String(Constants.DIAMETER_AVP_ORIGIN_REALM, ConfigService.realm));
			
			String sessionId = callParam.getSessionId();
			if(callParam.getServiceContextId().equals("RatableAuth.HSS@027.ctnbc-bon.net")){
				sessionId = sessionId.substring(0,sessionId.length()-1);
			}else if (callParam.getServiceContextId().equals("RatableResourceQuery.HWWH@027.ctnbc-bon.net")
					|| callParam.getServiceContextId().equals("RatableResourceQuery.HWHB@027.ctnbc-bon.net")
					|| callParam.getServiceContextId().equals("RatableResourceQuery.Balance@027.ctnbc-bon.net")) {
				sessionId = sessionId.substring(0,sessionId.length()-2);
			}
			
			answer.add(new AVP_UTF8String(263, sessionId));
			int serviceResultCode = callParam.getRetCode();
			int resultCode = serviceResultCode > 10000 ? 2001 : serviceResultCode;
		    if(serviceResultCode== UsageLimitConfigService.getIntValue("DIAMETER_RESULT_TOO_BUSY", 501)||serviceResultCode==LimitConfigService.getIntValue("DIAMETER_RESULT_TOO_BUSY", 501)){
			     resultCode = 2001;
			}
		    answer.add(new AVP_Unsigned32(268, resultCode));
		    answer.add(DccAvpHelper.genDefaultServiceResultAVP(serviceResultCode));
			
		    callParam.setResponse(answer);
		    callParam.setRetCode(callParam.getRetCode());
		    callParam.setResultMsg(callParam.getResultMsg());
		}
		
		return callParam;
	}
	
	
	/**
	 * 
	 * @ 说     明：通过AVPCODE删除AVP，返回对应的删除索引位置	 
	 * @param：newMessage  Message
	 * @param：_AvpCode  int
	 * @return：返回对应的删除索引位置  0：无此AVP
	 */
	public static int removeAvpByAvpCode(Message newMessage,int _AvpCode){
		int _ind_removeAvp = -1;
		_ind_removeAvp  = newMessage.findFirst(_AvpCode);
		if( -1 != _ind_removeAvp){
			newMessage.remove(_ind_removeAvp);
		}
		return _ind_removeAvp;
	}
	
	public int proccessResponse(CallParam callParam) {
		callParam.getAsynHandler().onCallResult(callParam);
		return 0;
	}

}
